package com.netty.esc.Interceptor.aspect;

import com.alibaba.fastjson.JSON;
import com.netty.esc.Interceptor.annotation.DontFuckMe;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 方法记录切面
 * @Fuck 自定义注解实现
 */
@Aspect
@Component
@Slf4j(topic = "esc-im 方法日志记录aop-------")
@Order(Integer.MAX_VALUE-1)
public class FuckAllAspectImpl {
    /**
     * PointCut
     */
    @Pointcut("@within(com.netty.esc.Interceptor.annotation.FuckAll) && within(com.netty.esc..*)")
    public void pointCut(){}

    /**
     * 前置通知（Before advice） ：在某连接点（JoinPoint）之前执行的通知，但这个通知不能阻止连接点前的执行。
     */
    @Before("pointCut()")
    @Deprecated
    public void doBefore(JoinPoint joinPoint){
        //2
        //该部分已被环绕增强替代，暂时没用
    }

    /**
     * 后通知（After advice） ：当某连接点退出的时候执行的通知（不论是正常返回还是异常退出）。
     */
    @AfterReturning("pointCut()")
    @Deprecated
    public void doAfter(JoinPoint joinPoint){
        //3
        //该部分已被环绕增强替代，暂时没用
    }

    /**
     * 抛出异常后通知（After throwing advice） ： 在方法抛出异常退出时执行的通知。
     */
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void doAfter(JoinPoint joinPoint,Exception e){
        //3
        //方法执行异常 log记录
        logHandler(joinPoint,e,"","");
    }

    /**
     * 环绕通知（Around advice） ：包围一个连接点的通知，类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为，也可以选择不执行。
     */
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //1

        final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        final Method method = signature.getMethod();
        final DontFuckMe annotation = method.getAnnotation(DontFuckMe.class);

        Object proceed = null;
        if (null!=annotation) {
            //不等于空则不执行日志记录
            proceed =joinPoint.proceed();
        }else {
//            Timer.begin();
            long start = System.currentTimeMillis();
            //如果出现异常，则有异常后置处理直接走异常后置处理增强方法，不走该后续部分
            //如果没有异常后置处理，则直接抛出异常
            proceed =joinPoint.proceed();
            long end = System.currentTimeMillis();
            logHandler(joinPoint,end-start+"",proceed);
        }
        return proceed;
    }


    /**
    * 日志处理
    */
    private void logHandler(JoinPoint joinPoint,Exception e,String cost,Object result){
        //方法名
        final String methodName = joinPoint.getSignature().getName();
        //类全路径
        final String referenceName = joinPoint.getSignature().getDeclaringTypeName();

        if (null==e){
            log.info("执行方法:{}()完毕,参数:{},结果:{},执行耗时:{}ms",referenceName+"."+methodName, getParamsMap(joinPoint),JSON.toJSONString(result),cost);
        }else{
            log.error("执行方法:{}()异常,参数:{},信息:{}",referenceName+"."+methodName,getParamsMap(joinPoint),e.getMessage());
        }
    }

    private void logHandler(JoinPoint joinPoint,String cost,Object result){
        logHandler(joinPoint,null,cost,result);
    }

    /**
     * 获取参数Map集合
     * @param joinPoint
     * @return
     */
    Map<String, Object> getParamsMap(JoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            param.put(paramNames[i], paramValues[i]);
        }
        return param;
    }


}
